package com.household.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.household.domain.ResponseResult;
import com.household.domain.entity.User;
import com.household.domain.enums.ResponseErrorCodeEnum;
import com.household.domain.enums.RoleEnum;
import com.household.domain.enums.SexEnum;
import com.household.domain.vo.AdminUserVO;
import com.household.domain.vo.BootstrapTableVO;
import com.household.domain.vo.UserInfoVO;
import com.household.mapper.UserMapper;
import com.household.service.UserService;
import com.household.utils.CollectionUtils;
import com.household.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.servlet.http.HttpSession;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Created By wanzhen
 * 2023. 2023/3/25. 11:43
 */

@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    private final UserMapper userMapper;

    private final DataSourceTransactionManager transactionManager;

    private final PasswordEncoder passwordEncoder;

    @Autowired
    public UserServiceImpl(UserMapper userMapper, DataSourceTransactionManager transactionManager, PasswordEncoder passwordEncoder) {
        this.userMapper = userMapper;
        this.transactionManager = transactionManager;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public ResponseResult registryByUsername(String username, String password) {
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);

        if (checkExistByUsername(username)) {
            transactionManager.rollback(status);
            return ResponseResult.failure(ResponseErrorCodeEnum.USERNAME_EXIST);
        }

        User user = User.builder()
                .username(username)
                .password(passwordEncoder.encode(password))
                .role(RoleEnum.DIS_FORMAL).build();
        if ((userMapper.insert(user)) != 1) {
            transactionManager.rollback(status);
            return ResponseResult.failure(ResponseErrorCodeEnum.REGISTRY_ERROR);
        }

        transactionManager.commit(status);
        return ResponseResult.success();
    }

    //    检查是否存在该用户
    @Override
    public boolean checkExistByUsername(String username) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        User user = userMapper.selectOne(queryWrapper);

        return user != null;

    }

    @Override
    public String getFrontName(String userId) {
        return getFrontName(getById(userId));
    }

    @Override
    public String getFrontName(User user) {
        String userName = null;
        if (StringUtils.isNoneBlank(user.getUsername())) {
            userName = user.getUsername();
        }
        return userName;
    }

    @Override
    public Map<String,Long> getAdminDashboardData() {
        Map<String, Long> map = new HashMap<>();
        Long todayCount = userMapper.selectCount(new QueryWrapper<User>().between("create_date", LocalDateTime.of(LocalDate.now(), LocalTime.MIDNIGHT), LocalDateTime.now()));

        Long totalCount = userMapper.selectCount(null);

        Long disEnableCount =
                userMapper.selectCount(new QueryWrapper<User>().eq("has_enable", 0));

        Long lockCount =
                userMapper.selectCount(new QueryWrapper<User>().gt("lock_date", LocalDateTime.now()));

        map.put("today", todayCount);
        map.put("total", totalCount);
        map.put("disEnable", disEnableCount);
        map.put("lock", lockCount);

        return map;

    }

    private AdminUserVO convert(User user) {
        AdminUserVO vo = AdminUserVO.builder()
                .id(user.getId())
                .username(getFrontName(user))
                .tel(user.getTel())
                .role(user.getRole().getType())
                .hasReal(!StringUtils.isAnyBlank(user.getRealName(), user.getIdCard()))
                .hasEnable(user.getHasEnable() == 1)
                .createDate(user.getCreateDate()).build();

        LocalDateTime lockDate = user.getLockDate();
        if(lockDate != null && LocalDateTime.now().isBefore(lockDate)) {
            vo.setLockDate(lockDate);
        }


        return vo;
    }

    private List<AdminUserVO> convert(List<User> users) {
        if(CollectionUtils.isListEmpty(users)) {
            return Collections.emptyList();
        }

        return users.stream().map(this::convert).collect(Collectors.toList());
    }

    @Override
    public BootstrapTableVO<AdminUserVO> pageAdminUserInfoVO(Page<User> page, QueryWrapper<User> wrapper) {
        IPage<User> selectPage = userMapper.selectPage(page, wrapper);
        BootstrapTableVO<AdminUserVO> result = new BootstrapTableVO<>();

        result.setTotal(selectPage.getTotal());
        result.setRows(convert(selectPage.getRecords()));

        return result;
    }

    @Override
    public UserInfoVO getUserInfo(String userId) {
        User user = getById(userId);
        RoleEnum userRole = user.getRole();
        UserInfoVO vo = UserInfoVO.builder()
                .username(user.getUsername())
                .sex(user.getSex().getCName())//getCname()
                .sexId(String.valueOf(user.getSex().getType()))
                .tel(user.getTel())
                .role(String.valueOf(userRole.getType()))
                .roleName(userRole.getCName())
                .idCard(user.getIdCard())
                .realName(user.getRealName()).build();
        return vo;
    }

    @Override
    public User getByTel(String tel) {
        List<User> userList = list(new QueryWrapper<User>().eq("tel", tel));
        return CollectionUtils.getListFirst(userList);
    }

    @Override
    public ResponseResult resetPassword(String userId, String oldPassword, String newPassword) {
        User user = getById(userId);

        if(!passwordEncoder.matches(oldPassword, user.getPassword())) {
            return ResponseResult.failure(ResponseErrorCodeEnum.PASSWORD_ERROR);
        }

        user.setPassword(passwordEncoder.encode(newPassword));

        if(!updateById(user)) {
            return ResponseResult.failure(ResponseErrorCodeEnum.PASSWORD_RESET_ERROR);
        }


        return ResponseResult.success();
    }

    @Override
    public ResponseResult setTel(User user, String tel, String code, HttpSession session) {
        if(checkExistByTel(tel)) {
            return ResponseResult.failure(ResponseErrorCodeEnum.TEL_EXIST);
        }

        user.setTel(tel);
        if(!updateById(user)) {
            return ResponseResult.failure(ResponseErrorCodeEnum.OPERATION_ERROR);
        }
        return ResponseResult.success();
    }

    @Override
    public boolean checkExistByTel(String mobile) {
        return getByTel(mobile) != null;
    }

    @Override
    public ResponseResult setSex(User sysUser, SexEnum sexEnum) {
        sysUser.setSex(sexEnum);
        if(!updateById(sysUser)) {
            return ResponseResult.failure(ResponseErrorCodeEnum.OPERATION_ERROR);
        }
        return ResponseResult.success();
    }
}
